1 /*
2 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.sun.media.sound;
26
27 import java.io.EOFException;
28 import java.io.IOException;
29 import java.io.InputStream;
30
31 import javax.sound.sampled.AudioFormat;
32 import javax.sound.sampled.AudioInputStream;
33
34 /**
35 * A jitter corrector to be used with SoftAudioPusher.
36 *
37 * @author Karl Helgason
38 */
39 public class SoftJitterCorrector extends AudioInputStream {
40
41 private static class JitterStream extends InputStream {
42
43 static int MAX_BUFFER_SIZE = 1048576;
44 boolean active = true;
45 Thread thread;
46 AudioInputStream stream;
47 // Cyclic buffer
48 int writepos = 0;
49 int readpos = 0;
50 byte[][] buffers;
51 Object buffers_mutex = new Object();
52
53 // Adapative Drift Statistics
54 int w_count = 1000;
55 int w_min_tol = 2;
56 int w_max_tol = 10;
57 int w = 0;
58 int w_min = -1;
59 // Current read buffer
60 int bbuffer_pos = 0;
61 int bbuffer_max = 0;
62 byte[] bbuffer = null;
63
64 public byte[] nextReadBuffer() {
65 synchronized (buffers_mutex) {
66 if (writepos > readpos) {
67 int w_m = writepos - readpos;
68 if (w_m < w_min)
69 w_min = w_m;
70
71 int buffpos = readpos;
72 readpos++;
73 return buffers[buffpos % buffers.length];
74 }
75 w_min = -1;
76 w = w_count - 1;
77 }
78 while (true) {
79 try {
80 Thread.sleep(1);
81 } catch (InterruptedException e) {
82 //e.printStackTrace();
83 return null;
84 }
85 synchronized (buffers_mutex) {
86 if (writepos > readpos) {
87 w = 0;
88 w_min = -1;
89 w = w_count - 1;
90 int buffpos = readpos;
91 readpos++;
92 return buffers[buffpos % buffers.length];
93 }
94 }
95 }
96 }
97
98 public byte[] nextWriteBuffer() {
99 synchronized (buffers_mutex) {
100 return buffers[writepos % buffers.length];
101 }
102 }
103
104 public void commit() {
105 synchronized (buffers_mutex) {
106 writepos++;
107 if ((writepos - readpos) > buffers.length) {
108 int newsize = (writepos - readpos) + 10;
109 newsize = Math.max(buffers.length * 2, newsize);
110 buffers = new byte[newsize][buffers[0].length];
111 }
112 }
113 }
114
115 public JitterStream(AudioInputStream s, int buffersize,
116 int smallbuffersize) {
117 this.w_count = 10 * (buffersize / smallbuffersize);
118 if (w_count < 100)
119 w_count = 100;
120 this.buffers
121 = new byte[(buffersize/smallbuffersize)+10][smallbuffersize];
122 this.bbuffer_max = MAX_BUFFER_SIZE / smallbuffersize;
123 this.stream = s;
124
125
126 Runnable runnable = new Runnable() {
127
128 public void run() {
129 AudioFormat format = stream.getFormat();
130 int bufflen = buffers[0].length;
131 int frames = bufflen / format.getFrameSize();
132 long nanos = (long) (frames * 1000000000.0
133 / format.getSampleRate());
134 long now = System.nanoTime();
135 long next = now + nanos;
136 int correction = 0;
137 while (true) {
138 synchronized (JitterStream.this) {
139 if (!active)
140 break;
141 }
142 int curbuffsize;
143 synchronized (buffers) {
144 curbuffsize = writepos - readpos;
145 if (correction == 0) {
146 w++;
147 if (w_min != Integer.MAX_VALUE) {
148 if (w == w_count) {
149 correction = 0;
150 if (w_min < w_min_tol) {
151 correction = (w_min_tol + w_max_tol)
152 / 2 - w_min;
153 }
154 if (w_min > w_max_tol) {
155 correction = (w_min_tol + w_max_tol)
156 / 2 - w_min;
157 }
158 w = 0;
159 w_min = Integer.MAX_VALUE;
160 }
161 }
162 }
163 }
164 while (curbuffsize > bbuffer_max) {
165 synchronized (buffers) {
166 curbuffsize = writepos - readpos;
167 }
168 synchronized (JitterStream.this) {
169 if (!active)
170 break;
171 }
172 try {
173 Thread.sleep(1);
174 } catch (InterruptedException e) {
175 //e.printStackTrace();
176 }
177 }
178
179 if (correction < 0)
180 correction++;
181 else {
182 byte[] buff = nextWriteBuffer();
183 try {
184 int n = 0;
185 while (n != buff.length) {
186 int s = stream.read(buff, n, buff.length
187 - n);
188 if (s < 0)
189 throw new EOFException();
190 if (s == 0)
191 Thread.yield();
192 n += s;
193 }
194 } catch (IOException e1) {
195 //e1.printStackTrace();
196 }
197 commit();
198 }
199
200 if (correction > 0) {
201 correction--;
202 next = System.nanoTime() + nanos;
203 continue;
204 }
205 long wait = next - System.nanoTime();
206 if (wait > 0) {
207 try {
208 Thread.sleep(wait / 1000000L);
209 } catch (InterruptedException e) {
210 //e.printStackTrace();
211 }
212 }
213 next += nanos;
214 }
215 }
216 };
217
218 thread = new Thread(runnable);
219 thread.setDaemon(true);
220 thread.setPriority(Thread.MAX_PRIORITY);
221 thread.start();
222 }
223
224 public void close() throws IOException {
225 synchronized (this) {
226 active = false;
227 }
228 try {
229 thread.join();
230 } catch (InterruptedException e) {
231 //e.printStackTrace();
232 }
233 stream.close();
234 }
235
236 public int read() throws IOException {
237 byte[] b = new byte[1];
238 if (read(b) == -1)
239 return -1;
240 return b[0] & 0xFF;
241 }
242
243 public void fillBuffer() {
244 bbuffer = nextReadBuffer();
245 bbuffer_pos = 0;
246 }
247
248 public int read(byte[] b, int off, int len) {
249 if (bbuffer == null)
250 fillBuffer();
251 int bbuffer_len = bbuffer.length;
252 int offlen = off + len;
253 while (off < offlen) {
254 if (available() == 0)
255 fillBuffer();
256 else {
257 byte[] bbuffer = this.bbuffer;
258 int bbuffer_pos = this.bbuffer_pos;
259 while (off < offlen && bbuffer_pos < bbuffer_len)
260 b[off++] = bbuffer[bbuffer_pos++];
261 this.bbuffer_pos = bbuffer_pos;
262 }
263 }
264 return len;
265 }
266
267 public int available() {
268 return bbuffer.length - bbuffer_pos;
269 }
270 }
271
272 public SoftJitterCorrector(AudioInputStream stream, int buffersize,
273 int smallbuffersize) {
274 super(new JitterStream(stream, buffersize, smallbuffersize),
275 stream.getFormat(), stream.getFrameLength());
276 }
277 }